iT邦幫忙

2023 iThome 鐵人賽

DAY 29
0
Modern Web

Phoenix 1.7 完全教學系列 第 29

29 LiveView_修改

  • 分享至 

  • xImage
  •  

這一回先由畫面開始

修改

在列表每一個 note 右邊加上修改按鈕

<.table id="notes" rows={@notes}>
  <:col :let={note} label="ID"><%= note.id %></:col>
  <:col :let={note} label="內容"><%= note.content %></:col>
  <:action :let={note}>
    <.link patch={~p"/#{note}/edit"}>修改</.link>
  </:action>
</.table>

由於 ~p 會去檢查目前的 router 有沒有相對應的 path,這裡會有沒有這個路徑的警告。

在 router 加上修改路徑,這裡的 :id 對應到 ~p"/#{note}/edit"#{note},寫 #{note.id} 也可以。

live "/:id/edit", NoteLive.Index, :edit

好了之後會發現其實已經可以按了,但是其實是新增。
所以要做的事情是修改 FormComponent

  1. 送進 changeset 的可以是目前的 note 而不是 新的 %Note{} struct
  2. 儲存的時候依照不同的 action 做新增或是更新

第一點的話我們可以在 NoteLive.Index 寫一個 :edit 專用的 handle_params 在裡面得到目前的 note

我們來改寫一下 handle_params 並使用我們自訂的 apply_action 來處理不同 action 時的行為

def handle_params(params, _uri, socket) do
  {:noreply, apply_action(socket, socket.assigns.live_action, params)}
end

defp apply_action(socket, :edit, %{"id" => id}) do
  {:ok, note} = Notes.get_note(id)

  socket
  |> assign(:page_title, "編輯感激筆記")
  |> assign(:note, note)
end

defp apply_action(socket, :new, _params) do
  socket
  |> assign(:page_title, "新增感激筆記")
  |> assign(:note, %Note{})
end

defp apply_action(socket, :index, _params) do
  socket
  |> assign(:page_title, "感激筆記列表")
  |> assign(:note, nil)
end

接著把 note 傳入 FormComponent

<.live_component
  module={GratitudeWeb.NoteLive.FormComponent}
  id={@note.id || :new}
  title={@page_title}
  action={@live_action}
  note={@note}
/>

(這邊除了 note 之外,也另外處理了不同 action 的標題)

傳入 note 之後 可以在 FormComponent update 的 params 拿到,修改 update

def update(%{note: note} = assigns, socket) do
  form =
    note
    |> Note.changeset(%{})
    |> to_form()

  socket =
    socket
    |> assign(assigns) # 將所有的 assigns 傳入 LiveComponent 裡的 assigns
    |> assign(form: form)

  {:ok, socket}
end

因為我們有額外處理 title,所以要在 update 的時候也把 title 傳入,所以可以更新 component 的 header

<.header>
  <%= @title %>
</.header>

最後則是要在 NoteLive.Indexhandle_event 裡面處理加上修改的儲存

將 handle_event 改為依照 action 作不同的處理

def handle_event("save", %{"note" => note_params}, socket) do
  save_note(socket, socket.assigns.action, note_params)
end

defp save_note(socket, :edit, note_params) do
  case Notes.update_note(socket.assigns.note, note_params) do
    {:ok, _note} ->
      send(self(), :saved)

      {:noreply,
       socket
       |> put_flash(:info, "修改成功")
       |> push_patch(to: ~p"/")}

    {:error, %Ecto.Changeset{} = changeset} ->
      {:noreply, assign(socket, form: to_form(changeset))}
  end
end

defp save_note(socket, :new, note_params) do
  case Notes.create_note(note_params) do
    {:ok, _note} ->
      send(self(), :saved)

      {:noreply,
       socket
       |> put_flash(:info, "新增成功")
       |> push_patch(to: ~p"/")}

    {:error, %Ecto.Changeset{} = changeset} ->
      {:noreply, assign(socket, form: to_form(changeset))}
  end
end

小測驗

寫一個修改功能的測試,寫簡單的快樂流程即可

test/gratitude_web/live/note_live_test.exs 加入另一個 test 即可

test "updates note in listing", %{conn: conn} do
  {:ok, note} = Notes.create_note(%{content: "感激筆記內容"})
  {:ok, view, _html} = live(conn, ~p"/")

  assert view |> element("a", "修改") |> render_click() =~ "編輯感激筆記"

  assert_patch(view, ~p"/#{note}/edit")

  assert view
         |> form("#note-form", note: %{content: "修改後的感激筆記內容"})
         |> render_submit()

  assert_patch(view, ~p"/")

  html = render(view)
  assert html =~ "修改成功"
  assert html =~ "修改後的感激筆記內容"
end

上一篇
28 LiveView 測試
下一篇
30 LiveView 刪除 與 30 天總結
系列文
Phoenix 1.7 完全教學30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言